package com.ccloud.common.redis.utils;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.ccloud.common.core.constant.RedisConstants;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * jedis工具类
 */
@Configuration
@Qualifier("JedisUtil")
public class JedisUtil {

    private static JedisPool jedisPool;

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.jedis.timeout}")
    private int timeout;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long maxWaitMillis;

    @Value("${spring.redis.password}")
    private String password;

    @Bean
    @PostConstruct
    public JedisPool redisPoolFactory() throws Exception {
        if (jedisPool == null) {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(maxIdle);
            jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
            jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
        }
        return jedisPool;
    }


    private static Jedis getJedis(int dbIndex) {
        Jedis jedis = jedisPool.getResource();
        jedis.select(dbIndex);
        return jedis;
    }

    private static <T> T doJedis(int i, Function<Jedis, T> fun) {
        try (Jedis jedis = getJedis(i)) {
            return fun.apply(jedis);
        }
    }

    /**
     * 根据key前缀匹配
     */
    public static Set<String> keys(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.keys(key));
    }

    /**
     * 根据key查看是否存在
     */
    public static boolean hasKey(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.exists(key));
    }

    /**
     * 在表头存放list
     */
    public static <T> void lpush(String key, List<T> list, int dbIndex) {
        lpush(key, list, dbIndex, RedisConstants.OUTTIME_DEFAULT);
    }

    /**
     * 在表头存放list, 设置过期时间
     */
    public static <T> void lpush(String key, List<T> list, int dbIndex, int timeout) {
        if (CollUtil.isNotEmpty(list)) {
            doJedis(dbIndex, e -> e.lpush(key, list.stream().map(JSONObject::toJSONString).toArray(String[]::new)));
            refreshLiveTime(key, timeout, dbIndex);
        }
    }

    /**
     * 在列表尾部存放list
     */
    public static <T> void rpush(String key, List<T> list, int dbIndex) {
        if (CollUtil.isNotEmpty(list)) {
            doJedis(dbIndex, e -> e.rpush(key, list.stream().map(JSONObject::toJSONString).toArray(String[]::new)));
            refreshLiveTime(key, RedisConstants.OUTTIME_DEFAULT, dbIndex);
        }
    }

    /**
     * 取出全部list
     */
    public static <T> List<T> lgetAll(String key, int dbIndex, Class<T> clazz) {
        List<String> ss = lgetAll(key, dbIndex);
        if (CollUtil.isEmpty(ss)) return Collections.emptyList();
        return ss.stream().map(e -> JSONObject.parseObject(e, clazz)).collect(Collectors.toList());
    }

    /**
     * 获取list，如果没有，设置进去，并设置过期时间
     */
    public static <T> List<T> lgetOrPush(String key, int dbIndex, Class<T> clazz, Supplier<List<T>> apply) {
        return lgetOrPush(key, dbIndex, RedisConstants.OUTTIME_DEFAULT, clazz, apply);
    }

    /**
     * 获取list，如果没有，设置进去，并设置过期时间
     */
    public static <T> List<T> lgetOrPush(String key, int dbIndex, int timeout, Class<T> clazz, Supplier<List<T>> apply) {
        List<T> list = lgetAll(key, dbIndex, clazz);
        if (CollUtil.isEmpty(list)) {
            list = apply.get();
            lpush(key, list, dbIndex);
            refreshLiveTime(key, timeout, dbIndex);
        }
        return list;
    }

    /**
     * 获取list，如果没有，在尾部设置进去，并设置过期时间
     */
    public static <T> List<T> lgetOrRpush(String key, int dbIndex, Class<T> clazz, Supplier<List<T>> apply) {
        return lgetOrRpush(key, dbIndex, RedisConstants.OUTTIME_DEFAULT, clazz, apply);
    }

    /**
     * 获取list，如果没有，在尾部设置进去，并设置过期时间
     */
    public static <T> List<T> lgetOrRpush(String key, int dbIndex, int timeout, Class<T> clazz, Supplier<List<T>> apply) {
        List<T> list = lgetAll(key, dbIndex, clazz);
        if (CollUtil.isEmpty(list)) {
            list = apply.get();
            rpush(key, list, dbIndex);
            refreshLiveTime(key, timeout, dbIndex);
        }
        return list;
    }

    /**
     * 取出全部list(String)
     */
    public static List<String> lgetAll(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.lrange(key, 0, e.ttl(key)));
    }

    /**
     * 取出范围内的list(String)
     */
    public static <T> List<T> lrange(String key, long start, long end, int dbIndex, Class<T> clazz) {
        List<String> ss = lrange(key, start, end, dbIndex);
        if (CollUtil.isEmpty(ss)) return Collections.emptyList();
        return ss.stream().map(e -> JSONObject.parseObject(e, clazz)).collect(Collectors.toList());
    }

    /**
     * 取出范围内的list(String)
     */
    public static List<String> lrange(String key, long start, long end, int dbIndex) {
        return doJedis(dbIndex, e -> e.lrange(key, start, end));
    }

    /**
     * 设置某个位置的值
     */
    public static <T> void lset(String key, long index, T val, int dbIndex) {
        String str = val instanceof String ? (String) val : JSONObject.toJSONString(val);
        doJedis(dbIndex, e -> e.lset(key, index, str));
    }

    /**
     * 设置set 的单个值
     */
    public static <T> void sadd(String key, T val, int dbIndex) {
        String str = val instanceof String ? (String) val : JSONObject.toJSONString(val);
        doJedis(dbIndex, e -> e.sadd(key, str));
    }

    /**
     * 设置set 的单个值 并设置过期时间
     */
    public static <T> void sadd(String key, T val, int timeout, int dbIndex) {
        String str = val instanceof String ? (String) val : JSONObject.toJSONString(val);
        doJedis(dbIndex, e -> e.sadd(key, str));
        refreshLiveTime(key, timeout, dbIndex);
    }

    /**
     * 设置set
     */
    public static <T> void sadd(String key, Set<T> set, int dbIndex) {
        if (CollUtil.isNotEmpty(set)) {
            String[] str = set.stream().map(val -> val instanceof String ? (String) val : JSONObject.toJSONString(val)).toArray(String[]::new);
            doJedis(dbIndex, e -> e.sadd(key, str));
        }
    }

    /**
     * 移除set 的单个值
     */
    @SafeVarargs
    public static <T> void srem(String key, int dbIndex, T... val) {
        String[] vals = new String[val.length];
        for (int i = 0; i < val.length; i++) {
            vals[i] = val[i] instanceof String ? (String) val[i] : JSONObject.toJSONString(val[i]);
        }
        doJedis(dbIndex, e -> e.srem(key, vals));
    }

    /**
     * 获取set 的单个值
     */
    public static Set<String> smembers(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.smembers(key));
    }

    /**
     * 获取set 的单个值
     */
    public static <T> Set<T> smembers(String key, int dbIndex, Class<T> clazz) {
        Set<String> ss = doJedis(dbIndex, e -> e.smembers(key));
        if (CollUtil.isEmpty(ss)) return Collections.emptySet();
        return ss.stream().map(e -> JSONObject.parseObject(e, clazz)).collect(Collectors.toSet());
    }

    /**
     * 获得列表的大小
     */
    public static long ttl(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.ttl(key));
    }

    /**
     * 设置hash 的map 并设置过期时间
     */
    public static <T> void hmset(String key, Map<String, T> hash, int timeout, int dbIndex) {
        Map<String, String> nh = new HashMap<>(hash.size());
        hash.forEach((k, v) -> nh.put(k, JSONObject.toJSONString(v)));
        hmsetStr(key, nh, dbIndex);
        refreshLiveTime(key, timeout, dbIndex);
    }

    /**
     * 设置hash 的map
     */
    public static <T> void hmset(String key, Map<String, T> hash, int dbIndex) {
        Map<String, String> nh = new HashMap<>(hash.size());
        hash.forEach((k, v) -> nh.put(k, JSONObject.toJSONString(v)));
        hmsetStr(key, nh, dbIndex);
    }

    /**
     * 设置hash 的String map
     */
    public static void hmsetStr(String key, Map<String, String> hash, int dbIndex) {
        doJedis(dbIndex, e -> e.hmset(key, hash));
        refreshLiveTime(key, RedisConstants.OUTTIME_DEFAULT, dbIndex);
    }

    /**
     * 设置hash 的单个数据
     */
    public static void hset(String key, String hkey, Object hval, int dbIndex) {
        String str = hval instanceof String ? (String) hval : JSONObject.toJSONString(hval);
        doJedis(dbIndex, e -> e.hset(key, hkey, str));
    }

    /**
     * 设置hash 的单个数据并设置过期时间
     */
    public static void hset(String key, String hkey, Object hval, int timeout, int dbIndex) {
        String str = hval instanceof String ? (String) hval : JSONObject.toJSONString(hval);
        doJedis(dbIndex, e -> e.hset(key, hkey, str));
        refreshLiveTime(key, timeout, dbIndex);
    }

    /**
     * 获取hash 的map ,返回自定义的value
     */
    public static <T> Map<String, T> hgetAll(String key, int dbIndex, Class<T> clazz) {
        Map<String, String> a = hgetAll(key, dbIndex);
        if (a == null || a.isEmpty()) return Collections.emptyMap();
        Map<String, T> nh = new HashMap<>();
        a.forEach((k, v) -> nh.put(k, JSONObject.parseObject(v, clazz)));
        return nh;
    }

    /**
     * 获取hash 的map ,返回自定义的value ，没有自己设置进去
     */
    public static <T> Map<String, T> hgetAllOrPush(String key, int dbIndex, Class<T> clazz, Supplier<Map<String, T>> apply) {
        return hgetAllOrPush(key, dbIndex, RedisConstants.OUTTIME_DEFAULT, clazz, apply);
    }

    /**
     * 获取hash 的map ,返回自定义的value ，没有自己设置进去
     */
    public static <T> Map<String, T> hgetAllOrPush(String key, int dbIndex, int timeout, Class<T> clazz, Supplier<Map<String, T>> apply) {
        Map<String, T> map = hgetAll(key, dbIndex, clazz);
        if (CollUtil.isEmpty(map)) {
            map = apply.get();
            Map<String, String> cache = new HashMap<>(map.size());
            map.forEach((k, v) -> cache.put(k, JSONObject.toJSONString(v)));
            hmsetStr(key, cache, dbIndex);
            refreshLiveTime(key, timeout, dbIndex);
        }
        return map;
    }

    /**
     * 获取hash 的map
     */
    public static Map<String, String> hgetAll(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.hgetAll(key));
    }

    /**
     * 获取hash 的单个数据 ,返回自定义的value
     */
    public static <T> T hget(String key, String hkey, int dbIndex, Class<T> clazz) {
        String s = doJedis(dbIndex, e -> e.hget(key, hkey));
        if (s == null) return null;
        return JSONObject.parseObject(s, clazz);
    }

    /**
     * 获取hash 的单个数据
     */
    public static String hget(String key, String hkey, int dbIndex) {
        return doJedis(dbIndex, e -> e.hget(key, hkey));
    }

    /**
     * 获取hash 的多个数据 ,返回自定义的value
     */
    public static <T> List<T> hmget(String key, int dbIndex, Class<T> clazz, String... hkey) {
        List<String> s = doJedis(dbIndex, e -> e.hmget(key, hkey));
        if (s == null) return Collections.emptyList();
        return s.stream().map(e -> JSONObject.parseObject(e, clazz)).collect(Collectors.toList());
    }

    /**
     * 获取hash 的多个数据
     */
    public static List<String> hmget(String key, int dbIndex, String... hkey) {
        return doJedis(dbIndex, e -> e.hmget(key, hkey));
    }

    /**
     * 设置key -value 形式数据
     */
    public static String set(String key, String value, int dbIndex) {
        return doJedis(dbIndex, e -> e.set(key, value));
    }

    /**
     * 设置key -value 形式数据,并设置过期时间（秒）
     */
    public static String set(String key, String value, int timeOut, int dbIndex) {
        return doJedis(dbIndex, e -> e.setex(key, timeOut, value));
    }

    /**
     * 根据key获取value
     */
    public static String getByKey(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.get(key));
    }

    /**
     * 根据通配符获取所有匹配的key
     */
    public static Set<String> getKesByPattern(String pattern, int dbIndex) {
        return doJedis(dbIndex, e -> e.keys(pattern));
    }

    /**
     * 根据key删除
     */
    public static void delByKey(String key, int dbIndex) {
        doJedis(dbIndex, e -> e.del(key));
    }

    /**
     * 根据多个key删除
     */
    public static void delByKey(int dbIndex, String... key) {
        doJedis(dbIndex, e -> e.del(key));
    }

    /**
     * 根据key删除单个数据
     */
    public static void hdel(String key, String hkey, int dbIndex) {
        doJedis(dbIndex, e -> e.hdel(key, hkey));
    }

    /**
     * 根据key删除多个数据
     */
    public static void hdel(String key, int dbIndex, String... hkey) {
        doJedis(dbIndex, e -> e.hdel(key, hkey));
    }

    /**
     * 根据key获取过期时间
     */
    public static long getTimeOutByKey(String key, int dbIndex) {
        return doJedis(dbIndex, e -> e.ttl(key));
    }

    /**
     * 清空数据 (当前库)【慎用啊！】
     */
    public static void flushDB(int dbIndex) {
        doJedis(dbIndex, BinaryJedis::flushDB);
    }

    /**
     * 刷新过期时间（秒）
     */
    public static void refreshLiveTime(String key, int timeOut, int dbIndex) {
        doJedis(dbIndex, e -> e.expire(key, timeOut));
    }

    /**
     * 移除某个数据
     */
    public static void remove(String keys, int dbIndex) {
        doJedis(dbIndex, e -> e.del(keys));
    }
}
